/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.bpm.engine.impl.cmd;

import com.alibaba.fastjson2.JSONObject;
import com.google.common.base.Strings;
import com.je.bpm.core.model.BpmnModel;
import com.je.bpm.core.model.MultiInstanceLoopCharacteristics;
import com.je.bpm.core.model.config.CounterSignPassTypeEnum;
import com.je.bpm.core.model.task.KaiteCounterSignUserTask;
import com.je.bpm.engine.ActivitiException;
import com.je.bpm.engine.delegate.DelegateExecution;
import com.je.bpm.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior;
import com.je.bpm.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import com.je.bpm.engine.impl.interceptor.CommandContext;
import com.je.bpm.engine.impl.persistence.entity.ExecutionEntity;
import com.je.bpm.engine.impl.persistence.entity.TaskEntity;
import com.je.bpm.engine.impl.util.ProcessDefinitionUtil;
import com.je.bpm.engine.task.DelegationState;
import com.je.bpm.engine.task.Task;
import com.je.bpm.engine.upcoming.UpcomingCommentInfoDTO;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 减签任务命令
 */
public class CountersignedVisaReductionCmd extends AbstractCompleteTaskCmd {

    private String taskId;

    private String assignee;

    public CountersignedVisaReductionCmd(String assignee, String taskId) {
        super(taskId, null, null);
        this.assignee = assignee;
        this.taskId = taskId;
    }

    @Override
    protected Void execute(CommandContext commandContext, TaskEntity task) {
        // 根据执行实例ID获取当前执行实例
        ExecutionEntity currentExecutionEntity = task.getExecution();

        BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(task.getProcessDefinitionId());
        KaiteCounterSignUserTask miActivityElement = (KaiteCounterSignUserTask) bpmnModel.getFlowElement(task.getTaskDefinitionKey());

        ExecutionEntity rootExecution = (ExecutionEntity) getMultiInstanceRootExecution(task.getExecution());
        check(miActivityElement, rootExecution);

        List<String> users = getListVariable(rootExecution, MultiInstanceActivityBehavior.PROCESSING_USERS_INFO);

        //重新设置人员
        users.remove(assignee);
        rootExecution.setVariableLocal(MultiInstanceActivityBehavior.PROCESSING_USERS_INFO, users);
        for (DelegateExecution execution : rootExecution.getExecutions()) {
            ExecutionEntity executionEntity = (ExecutionEntity) execution;
            List<TaskEntity> list = executionEntity.getTasks();
            for (TaskEntity task1 : list) {
                task1.setVariableLocal(MultiInstanceActivityBehavior.PROCESSING_USERS_INFO, users);
            }
        }
        //减签待办信息
        upcoming(task, commandContext);

        MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = miActivityElement.getLoopCharacteristics();
        //并行多实例
        if (!multiInstanceLoopCharacteristics.isSequential()) {
            ParallelMultiInstanceBehavior prallelMultiInstanceBehavior = (ParallelMultiInstanceBehavior) miActivityElement.getBehavior();
            List<ExecutionEntity> list = (List<ExecutionEntity>) rootExecution.getExecutions();
            ExecutionEntity deleteExecutionEntity = null;
            String deleteTaskId = "";
            TaskEntity deleteTaskEntity = null;
            for (ExecutionEntity executionEntity : list) {
                List<TaskEntity> taskEntityList = executionEntity.getTasks();
                for (TaskEntity taskEntity : taskEntityList) {
                    if (taskEntity.getAssignee().equals(assignee)) {
                        deleteTaskId = taskEntity.getId();
                        deleteTaskEntity = taskEntity;
                        deleteExecutionEntity = executionEntity;
                        break;
                    }
                }
                if (deleteExecutionEntity != null) {
                    break;
                }
            }
            //多实例总数-1
            int nrOfInstances = getLoopVariable(rootExecution, MultiInstanceActivityBehavior.NUMBER_OF_INSTANCES);
            rootExecution.setVariableLocal(MultiInstanceActivityBehavior.NUMBER_OF_INSTANCES, nrOfInstances - 1);
            //已完成实例的数量-1，因为后面会+1，在这里做一个平衡
            int nrOfCompletedInstances = getLoopVariable(rootExecution, MultiInstanceActivityBehavior.NUMBER_OF_COMPLETED_INSTANCES);
            rootExecution.setVariableLocal(MultiInstanceActivityBehavior.NUMBER_OF_COMPLETED_INSTANCES, nrOfCompletedInstances - 1);
            rootExecution.setVariableLocal(MultiInstanceActivityBehavior.COUNTERSIGN_PASS_TYPE, task.getExecution().getVariable(MultiInstanceActivityBehavior.COUNTERSIGN_PASS_TYPE));
            rootExecution.setVariableLocal(MultiInstanceActivityBehavior.COUNTERSIGN_VOTE_ALL, task.getExecution().getVariable(MultiInstanceActivityBehavior.COUNTERSIGN_VOTE_ALL));
            prallelMultiInstanceBehavior.leave(currentExecutionEntity);
            deleteTaskEntity.setDelegationState(DelegationState.RESOLVED);
            commandContext.getTaskEntityManager().update(deleteTaskEntity);
            deleteExecutionEntity.removeVariable(MultiInstanceActivityBehavior.PROCESSING_USERS_INFO);
            commandContext.getTaskEntityManager().deleteTask(deleteTaskEntity, null, false, false);
            commandContext.getHistoricTaskInstanceEntityManager().delete(deleteTaskId);
            commandContext.getExecutionEntityManager().deleteExecutionAndRelatedData(deleteExecutionEntity, null);
        } else {
            int nrOfInstances = getLoopVariable(rootExecution, MultiInstanceActivityBehavior.NUMBER_OF_INSTANCES) - 1;
            rootExecution.setVariable(MultiInstanceActivityBehavior.NUMBER_OF_INSTANCES, nrOfInstances);
            int loopCounter = getLoopVariable(rootExecution, "loopCounter");
            if (loopCounter >= nrOfInstances || completionConditionSatisfied(rootExecution)) {
                executeTaskComplete(commandContext, task, new HashMap<>(), true);
                commandContext.getHistoricVariableInstanceEntityManager().updateDatas(task.getExecutionId(), JSONObject.toJSONString(users));
                commandContext.getHistoricTaskInstanceEntityManager().delete(task.getId());
            } else if (task.getAssignee().equals(assignee)) {
                int nrOfCompletedInstances = getLoopVariable(rootExecution, MultiInstanceActivityBehavior.NUMBER_OF_COMPLETED_INSTANCES);
                rootExecution.setVariableLocal(MultiInstanceActivityBehavior.NUMBER_OF_COMPLETED_INSTANCES, nrOfCompletedInstances - 1);
                rootExecution.setVariableLocal("loopCounter", loopCounter - 1);
                // 删除执行流程
                executeTaskComplete(commandContext, task, new HashMap<>(), true);
                commandContext.getHistoricTaskInstanceEntityManager().delete(task.getId());
            }
        }
        return null;
    }

    public boolean completionConditionSatisfied(DelegateExecution execution) {
        //会签类型 比例通过制、负责人决定
        String type = getStringVariable(execution, MultiInstanceActivityBehavior.COUNTERSIGN_PASS_TYPE);
        //全部处理
        Boolean voteAll = getBooleanVariable(execution, MultiInstanceActivityBehavior.COUNTERSIGN_VOTE_ALL);
        if (Strings.isNullOrEmpty(type) || voteAll) {
            return false;
        }

        //比例通过
        if (type.equals(CounterSignPassTypeEnum.PASS_PERSENT.toString())) {
            //通过比例
            int passAmount = getLoopVariable(execution, MultiInstanceActivityBehavior.NUMBER_OF_ACTIVE_AMOUNT);
            //未通过比例
            int vetoAmount = 100 - passAmount;
            if (vetoAmount == 0) {
                return false;
            }
            //多实例总数 -- 总处理数
            int nrOfInstances = getLoopVariable(execution, MultiInstanceActivityBehavior.NUMBER_OF_INSTANCES);
            //通过数量
            int nrOfPassInstances = getLoopVariable(execution, MultiInstanceActivityBehavior.NUMBER_OF_PASS_INSTANCES);
            //否决数量
            int nrOfVetoInstances = getLoopVariable(execution, MultiInstanceActivityBehavior.NUMBER_OF_VETO_INSTANCES);
            //弃权数量
            int nrOfAbstainInstances = getLoopVariable(execution, MultiInstanceActivityBehavior.NUMBER_OF_ABSTAIN_INSTANCES);
            //通过数量*100/实际处理数量 >= 通过比例
            if (nrOfPassInstances * 100 / (nrOfInstances - nrOfAbstainInstances) >= passAmount) {
                return true;
            }
            //否决数量*100/实际处理数量 >= 未通过比例
            if (nrOfVetoInstances != 0) {
                if (nrOfVetoInstances * 100 / (nrOfInstances - nrOfAbstainInstances) >= vetoAmount) {
                    return true;
                }
            }
            return false;
        }
        //负责人
        if (type.equals(CounterSignPassTypeEnum.PASS_PRINCIPAL.toString())) {
            String principalOpinion = getStringVariable(execution, MultiInstanceActivityBehavior.PRINCIPAL_OPINION);
            if (!Strings.isNullOrEmpty(principalOpinion)) {
                return true;
            }
        }
        return false;
    }

    protected Boolean getBooleanVariable(DelegateExecution execution, String variableName) {
        Object value = execution.getVariable(variableName);
        return (Boolean) (value != null ? value : false);
    }


    protected Integer getLoopVariable(DelegateExecution execution, String variableName) {
        Object value = execution.getVariableLocal(variableName);
        DelegateExecution parent = execution.getParent();
        while (value == null && parent != null) {
            value = parent.getVariableLocal(variableName);
            parent = parent.getParent();
        }
        return (Integer) (value != null ? value : 0);
    }

    public String getTaskId() {
        return taskId;
    }

    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }

    public String getAssignee() {
        return assignee;
    }

    public void setAssignee(String assignee) {
        this.assignee = assignee;
    }

    protected DelegateExecution getMultiInstanceRootExecution(DelegateExecution executionEntity) {
        DelegateExecution multiInstanceRootExecution = null;
        DelegateExecution currentExecution = executionEntity;
        while (currentExecution != null && multiInstanceRootExecution == null && currentExecution.getParent() != null) {
            if (currentExecution.isMultiInstanceRoot()) {
                multiInstanceRootExecution = currentExecution;
            } else {
                currentExecution = currentExecution.getParent();
            }
        }
        return multiInstanceRootExecution;
    }

    private void upcoming(Task task, CommandContext commandContext) {
        UpcomingCommentInfoDTO upcomingInfo = UpcomingCommentInfoDTO.build(SubmitTypeEnum.REDUCE, task.getTaskDefinitionKey(),
                task.getProcessInstanceId());
        Map<String, String> params = new HashMap<>();
        params.put("user", assignee);
        upcomingInfo.setParams(params);
        commandContext.getProcessEngineConfiguration().getActivitiUpcomingRun().completeUpcoming(upcomingInfo);
    }

    private void check(KaiteCounterSignUserTask miActivityElement, ExecutionEntity miExecution) {

        if (miActivityElement.getLoopCharacteristics() == null) {
            throw new ActivitiException("不是多实例节点,无法减签!");
        }

        List<String> users = getListVariable(miExecution, MultiInstanceActivityBehavior.PROCESSING_USERS_INFO);
        if (!users.contains(assignee)) {
            throw new ActivitiException(String.format("%s:不存在，无法减签！", assignee));
        }

        JSONObject processingInfo = getJsonObject(miExecution, MultiInstanceActivityBehavior.PROCESSING_INFO);
        if (processingInfo != null) {
            HashMap<String, String> opinions = (HashMap<String, String>) processingInfo.get("opinions");
            Object opinion = opinions.get(assignee);
            if (opinion != null) {
                throw new ActivitiException(String.format("%s:已审批，无法减签！", assignee));
            }
        }
    }

}
